from flask import Blueprint, request, current_app, g
import os
import time
from hashlib import md5
from utils import restful
from flask_jwt_extended import jwt_required, get_jwt_identity
from .forms import UploadImageForm, AddBannerForm, EditBannerForm
from models.auth import UserModel, Permission
from models.post import BannerModel, PostModel, CommentModel, BoardModel
from exts import db
from sqlalchemy.sql import func
from datetime import datetime, timedelta
from .decorators import permission_required


bp = Blueprint("cmsapi", __name__, url_prefix="/cmsapi")


# 在请求之前，判断用户是否登录
@bp.before_request # 这个装饰器是全局的，所有请求都会经过这个装饰器
@jwt_required() #
# 处理CMS API请求的预处理函数
def cmsapi_before_request():
    """
    jwt_required() 装饰的路由时，Flask-JWT-Extended 会自动验证请求中的 JWT 是否有效，包括检查签名是否正确、是否过期等。

    get_jwt_identity() 是 Flask-JWT-Extended 提供的一个函数，用于从当前请求的 JWT 中提取用户的身份信息。
    当一个请求通过了 jwt_required() 装饰器的验证后，你可以在视图函数中调用 get_jwt_identity() 来获取用户的身份信息。
    :return:
    """
    if request.method == 'OPTIONS':
        return
    identity = get_jwt_identity()
    user = UserModel.query.filter_by(id=identity).first()
    if user:
        # 设置对象g的属性"user"为user    setattr(x, 'y', v) is equivalent to ``x.y = v``      他是一个 属性字典
        setattr(g, "user", user)



@bp.get("/")
@jwt_required()
def mytest():
    # 这个identify是当初通过create_access_token传入的identity
    # create_access_token是最开始登录的时候 判断是否为员工而创建的的一个token
    identity = get_jwt_identity() # 这个函数会返回当初通过create_access_token传入的identity
    return restful.ok(message="success", data={"identity": identity})


@bp.post("/banner/image/upload")
@permission_required(Permission.BANNER)
def upload_banner_image():
    """
    上传轮播图图片
    :return:
    """
    form = UploadImageForm(request.files) # request.files是用户上传的文件
    if form.validate(): # 如果表单验证通过
        # form.image.data是用户上传的文件
        image = form.image.data # image是用户上传的文件
        # 不要使用用户上传上来的文件名，否则容易被黑客攻击
        filename = image.filename
        # xxx.png,xx.jpeg
        _, ext = os.path.splitext(filename) # ext是文件后缀名 xxx.xxx    ext = .xxx
        filename = md5((g.user.email + str(time.time())).encode("utf-8")).hexdigest() + ext
        image_path = os.path.join(current_app.config['BANNER_IMAGE_SAVE_PATH'], filename) # image_path是保存图片的路径
        image.save(image_path) # 保存图片
        return restful.ok(data={"image_url": filename})
    else:
        message = form.messages[0] # 获取第一个错误信息
        return restful.params_error(message=message) # 如果表单验证不通过，返回错误信息


@bp.post("/banner/add")
@permission_required(Permission.BANNER)
def add_banner(): # 添加轮播图
    form = AddBannerForm(request.form) # request.form是用户提交的表单数据
    if form.validate(): # 如果表单验证通过
        name = form.name.data # 获取表单数据
        image_url = form.image_url.data # 获取表单数据
        link_url = form.link_url.data # 获取表单数据
        priority = form.priority.data # 获取表单数据
        banner_model = BannerModel(name=name, image_url=image_url, link_url=link_url, priority=priority) # 创建轮播图模型
        db.session.add(banner_model) # 添加到数据库
        db.session.commit() # 提交数据库
        return restful.ok(data=banner_model.to_dict()) # 返回轮播图数据
    else:
        return restful.params_error(message=form.messages[0]) # 如果表单验证不通过，返回错误信息


@bp.get("/banner/list")
@permission_required(Permission.BANNER)
def banner_list(): # 获取轮播图列表
    banners = BannerModel.query.order_by(BannerModel.create_time.desc()).all()   # 获取所有轮播图
    # BannerModel
    banner_dicts = [banner.to_dict() for banner in banners] # 将轮播图模型转换为字典
    return restful.ok(data=banner_dicts) # 返回轮播图数据


@bp.post("/banner/delete")
@permission_required(Permission.BANNER)
def delete_banner():
    banner_id = request.form.get("id") # 获取表单数据
    if not banner_id: # 如果没有传入id
        return restful.params_error(message="没有传入id！") # 返回错误信息
    try:
        banner_model = BannerModel.query.get(banner_id) # 获取轮播图模型
    except Exception as e:
        return restful.params_error(message="此轮播图不存在！") # 返回错误信息
    db.session.delete(banner_model) #   删除轮播图模型
    db.session.commit() # 提交数据库
    return restful.ok() # 返回成功信息


@bp.post("/banner/edit")
@permission_required(Permission.BANNER)
def edit_banner():
    form = EditBannerForm(request.form) # 获取表单数据
    if form.validate(): # 如果表单验证通过
        banner_id = form.id.data # 获取表单数据
        try: # 如果没有传入id
            banner_model = BannerModel.query.get(banner_id) # 获取轮播图模型
        except Exception as e: #      如果没有传入id
            return restful.params_error(message="轮播图不存在！") #     or(message="此轮播图不存在！") # 返回错误信息
        name = form.name.data  # 获取表单数据
        image_url = form.image_url.data # 获取表单数据
        link_url = form.link_url.data # 获取表单数据
        priority = form.priority.data #       获取表单数据

        banner_model.name = name # 修改轮播图模型
        banner_model.image_url = image_url  # 修改轮播图模型
        banner_model.link_url = link_url # 修改轮播图模型
        banner_model.priority = priority # 修改轮播图模型
        db.session.commit()  #   提交数据库
        return restful.ok(data=banner_model.to_dict()) # 返回轮播图数据
    else:
        return restful.params_error(message=form.messages[0]) # (message="此轮播图不存在！") # 返回错误信息


@bp.get("/post/list")
@permission_required(Permission.POST)
def post_list():
    page = request.args.get('page', default=1, type=int) # 获取页码
    per_page_count = current_app.config['PER_PAGE_COUNT'] # 获取总页数
    start = (page-1)*per_page_count # 起始位置
    end = start + per_page_count # 结束位置
    query_obj = PostModel.query.order_by(PostModel.create_time.desc()) # 根据倒数顺序获取所有帖子
    total_count = query_obj.count() # 获取总条数
    posts = query_obj.slice(start, end) # 获取所有帖子
    # [PostModel(1),PostModel(2)]
    post_list = [post.to_dict() for post in posts]  # 将帖子模型转换为字典
    return restful.ok(data={
        'total_count': total_count,
        "post_list": post_list,
        "page": page
    })


@bp.post("/post/delete")
@permission_required(Permission.POST)
def delete_post():
    """
    删除帖子
    :return:
    """
    post_id = request.form.get("id")
    try:
        post_model = PostModel.query.get(post_id)
    except Exception as e:
        return restful.params_error(message="帖子不存在！")
    db.session.delete(post_model)
    db.session.commit()
    return restful.ok()


@bp.get("/comment/list")
@permission_required(Permission.COMMENT)
def comment_list():
    """
    评论列表
    :return:
    """
    comments = CommentModel.query.order_by(CommentModel.create_time.desc()).all()
    comment_list = []
    for comment in comments:
        comment_dict = comment.to_dict()
        comment_list.append(comment_dict)
    return restful.ok(data=comment_list)


@bp.post("/comment/delete")
@permission_required(Permission.COMMENT)
def delete_comment():
    """
    删除评论
    :return:
    """
    comment_id = request.form.get("id")
    CommentModel.query.filter_by(id=comment_id).delete()
    return restful.ok()


@bp.get("/user/list")
@permission_required(Permission.USER)
def user_list():
    """
    用户列表
    :return:
    """
    users = UserModel.query.order_by(UserModel.join_time.desc()).all()
    user_list = [user.to_dict() for user in users]
    return restful.ok(data=user_list)


@bp.post("/user/active")
@permission_required(Permission.USER)
def active_user():
    """
    激活用户
    :return:
    """
    is_active = request.form.get('is_active', type=int)
    user_id = request.form.get("id")
    user = UserModel.query.get(user_id)
    user.is_active = bool(is_active)
    db.session.commit()
    return restful.ok(data=user.to_dict())


@bp.get("/board/post/count")
def board_post_count():
    # 要获取板块下的帖子数量
    """

    :return:
    """
    board_post_count_list = db.session.query(BoardModel.name, func.count(BoardModel.name)).join(PostModel).group_by(BoardModel.name).all()
    board_names = [] # 板块列表
    post_counts = [] # 帖子数量
    for board_post_count in board_post_count_list:
        board_names.append(board_post_count[0])
        post_counts.append(board_post_count[1])
    return restful.ok(data={"board_names": board_names, "post_counts": post_counts})


@bp.get("/day7/post/count")
def day7_post_count():
    """
    7天帖子数量
    :return:
    """
    # 日期，帖子的数量
    # MySQL数据库：用的是date_format函数
    # SQlite数据库：用的是strfformat函数
    now = datetime.now()
    # 减6天，就是近7天的帖子
    # 时间的增、减
    # 一定要把时分秒毫秒都要减为0，不然就只能获取到7天前当前时间的帖子
    seven_day_ago = now - timedelta(days=6, hours=now.hour, minutes=now.minute, seconds=now.second, microseconds=now.microsecond)
    # [('2021-11-27', 99)]
    day7_post_count_list = db.session.query(func.date_format(PostModel.create_time, "%Y-%m-%d"), func.count(PostModel.id)).group_by(func.date_format(PostModel.create_time, "%Y-%m-%d")).filter(PostModel.create_time >= seven_day_ago).all()
    day7_post_count_dict = dict(day7_post_count_list)
    for x in range(7):
        date = seven_day_ago + timedelta(days=x)
        date_str = date.strftime("%Y-%m-%d")
        if date_str not in day7_post_count_dict:
            day7_post_count_dict[date_str] = 0
    dates = sorted(list(day7_post_count_dict.keys()))
    counts = []
    for date in dates:
        counts.append(day7_post_count_dict[date])
    data = {"dates": dates, "counts": counts}
    return restful.ok(data=data)